﻿/*
 * Mtbl.h
 *
 */

#ifndef __GDK_MTBL_H__
#define __GDK_MTBL_H__


#include <gdk/core/Types.h>
#include <gdk/core/cpptypeid.h>
#include <gdk/core/Exception.h>
#include <gdk/core/Stream.h>


namespace gdk
{


	////////////////////////////////////////////////////
	struct Mtbl
	{
		typedef Void			Class;
		typedef void* Class::*	Pointer;
		typedef Class& (*Read)(Class&, Class&);
		typedef Class& (*Write)(Class&, const Class&);

		Pointer	_pointer;
		Read	_read;
		Write	_write;
	};


	////////////////////////////////////////////////////
	GDK_CORE_EXPORT Void& readMtbl(Void &is, Void &o, Mtbl const *mtbl) __THROW1;
	GDK_CORE_EXPORT Void& writeMtbl(Void &os, Void const &o, Mtbl const *mtbl) __THROW1;


	////////////////////////////////////////////////////
	struct MtblHelper
	{
		typedef Mtbl::Class		Class;
		typedef Mtbl::Pointer	Pointer;
		typedef Mtbl::Read		Read;
		typedef Mtbl::Write		Write;


		template<typename TyClass, typename Ty> __INLINE__ static Pointer getMemberPointer(Ty TyClass::*p)
		{
			//TyClass虚拟继承父类后 sizeof(Ty TyClass::*)==8
			//GDK_ASSERT( sizeof(Ty TyClass::*) == sizeof(Pointer) );

			struct {
				union {
					Ty TyClass::*	_ptr;
					Pointer			_pointer;
				};
			} r;
			r._ptr = p;

			return r._pointer;
		}
		template<typename TyClass, typename TySupper> __INLINE__ static Pointer getSupperClassPointer(void)
		{
			//TyClass虚拟继承父类后 sizeof(Ty TyClass::*)==8
			//GDK_ASSERT( sizeof(void*) == sizeof(Pointer) );

			//检查是否基类,子类指针可以将地址赋给父类的指针(编译时检查)
			TySupper * const p = (TyClass*)(0);
			(void)p; //屏蔽编译警告
			//检查是否是相同的类,同类型的指针也可以相互赋值,上一代码检查不出是否真的为父类,再进行是否相同的类(运行时检查)
			GDK_ASSERT( "not super class." && typeid_className<TySupper>() != typeid_className<TyClass>() );

			struct {
				union {
					void*	_ptr;
					Pointer	_pointer;
				};
			} r;
			r._ptr = (void*)((char*)(TySupper*)1 - (char*)(TyClass*)(TySupper*)1);

			return r._pointer;
		}

		template<typename Ty> static gdk::InputStream& read_(gdk::InputStream &is, Ty &o) { return is >> o; }
		template<typename Ty> static gdk::OutputStream& write_(gdk::OutputStream &os, const Ty &o) { return os << o; }

		template<typename TyClass, typename Ty> __INLINE__ static Read  getSerializeReadHandler(Ty TyClass::*)
		{
			typedef typename gdk::InputStream&(*Read_)(gdk::InputStream&, Ty&);
			return (Read)(Read_)(&MtblHelper::read_);
		}
		template<typename TyClass, typename Ty> __INLINE__ static Write getSerializeWriteHandler(Ty TyClass::*)
		{
			typedef typename gdk::OutputStream&(*Write_)(gdk::OutputStream&, const Ty&);
			return (Write)(Write_)(&MtblHelper::write_);
		}

		template<typename TyClass> __INLINE__ static Read  getSerializeReadHandler(void)
		{
			typedef typename gdk::InputStream&(*Read_)(gdk::InputStream&, TyClass&);
			return (Read)(Read_)(&MtblHelper::read_);
		}
		template<typename TyClass> __INLINE__ static Write getSerializeWriteHandler(void)
		{
			typedef typename gdk::OutputStream&(*Write_)(gdk::OutputStream&, const TyClass&);
			return (Write)(Write_)(&MtblHelper::write_);
		}
	};



	////////////////////////////////////////////////////////
#ifdef _DEBUG

#	define DECLARE_MTBL_CHECK(Class) \
		public:\
			static void chkMtbl_(void);\
		private:

#	define IMPLEMENT_MTBL_CHECK(Class) \
		void Class::chkMtbl_(void)\
		{\
			for (const gdk::Mtbl *m1 = _mtbl; m1->_pointer != 0; ++m1) {\
				for (const gdk::Mtbl *m2 = m1 + 1; m2->_pointer != 0; ++m2) {\
					GDK_ASSERT( "class " #Class " Mtbl redefinition" && m1->_pointer != m2->_pointer );\
				}\
			}\
		}\
		static const int g_##Class##_chkMtbl_ = (::atexit(Class::chkMtbl_));

#else

#	define DECLARE_MTBL_CHECK(Class)
#	define IMPLEMENT_MTBL_CHECK(Class)

#endif


#define DECLARE_MTBL(Class) \
			DECLARE_MTBL_CHECK(Class)\
		private:\
			typedef Class ThisClass;\
		private:\
			static gdk::Mtbl const _mtbl[];\
		public:\
			__INLINE__ friend gdk::InputStream& operator >>(gdk::InputStream &is, Class &o) __THROW1\
				{ return (gdk::InputStream&)gdk::readMtbl((gdk::Void&)is, (gdk::Void&)o, Class::_mtbl); }\
			__INLINE__ friend gdk::OutputStream& operator <<(gdk::OutputStream &os, const Class &o) __THROW1\
				{ return (gdk::OutputStream&)gdk::writeMtbl((gdk::Void&)os, (gdk::Void const&)o, Class::_mtbl); }\
		private:

#define IMPLEMENT_MTBL_BEGIN(Class)		IMPLEMENT_MTBL_CHECK(Class)\
										gdk::Mtbl const Class::_mtbl[] = \
										{
#define IMPLEMENT_MTBL_SUPPER(Supper)		{	gdk::MtblHelper::getSupperClassPointer<ThisClass,Supper>(),\
												gdk::MtblHelper::getSerializeReadHandler<Supper>(),\
												gdk::MtblHelper::getSerializeWriteHandler<Supper>()\
											},
#define IMPLEMENT_MTBL_MEMBER(Member)		{	gdk::MtblHelper::getMemberPointer(&ThisClass::Member),\
												gdk::MtblHelper::getSerializeReadHandler(&ThisClass::Member),\
												gdk::MtblHelper::getSerializeWriteHandler(&ThisClass::Member)\
											},
#define IMPLEMENT_MTBL_END()				{NULL, NULL, NULL}\
										};




}




#endif //__GDK_MTBL_H__
